#include "projectnavigator.h"

#include "../dialogs/xmiimportdialog.h"
#include "../documents/swpprojectdocument.h"
#include "../mainwindow.h"
#include "../utils/mimetypemanager.h"
#include "../swpcommon.h"

#include <coremimetypes.h>
#include <fspersistencecommon.h>
#include <iconmanager.h>
#include <ilinkeddocument.h>
#include <istorage.h>
#include <lineeditdelegate.h>
#include <linkseditordialog.h>
#include <persistence.h>
#include <saveevent.h>
#include <validatormanager.h>

#include <QtCore/QEvent>
#include <QtCore/QStringList>
#include <QtCore/QUrl>
#include <QtGui/QAction>
#include <QtGui/QContextMenuEvent>
#include <QtGui/QDragEnterEvent>
#include <QtGui/QDragMoveEvent>
#include <QtGui/QMenu>
#include <QtGui/QMessageBox>
#include <QtGui/QToolBar>

using namespace core;
using namespace fspersistence;
using namespace gui;
using namespace swp;

typedef QSharedPointer<IDocument> Document;

ProjectNavigator::ProjectNavigator(
    const QList<QAction* >& projectDependentActions,
    QWidget* parent) : QTreeWidget(parent),
        IEditor(),
        m_inDrag(false),
        m_draggedIsContainer(false),
        m_draggedItem(0),
        m_itemToDocumentMapping(),
        m_openedProjectDependentActions(projectDependentActions),
        m_contextMenu(0),
        m_closeAction(0),
        m_renameAction(0),
        m_addDocumentAction(0),
        m_addDocumentMenuAction(0),
        m_removeAction(0),
        m_deleteAction(0),
        m_collapseAll(0),
        m_expandAll(0),
        m_setLinks(0)
{
    LineEditDelegate* delegate = new LineEditDelegate;
    delegate->setRole(Qt::UserRole);
    delegate->setValidator(ValidatorManager::getInstance()->getValidator(
        DOCUMENT_NAME_VALIDATOR));

    headerItem()->setHidden(true);
    setDragEnabled(true);
    setDragDropMode(QAbstractItemView::InternalMove);
    setItemDelegate(delegate);
    setEditTriggers(QAbstractItemView::EditKeyPressed);

    IconManager* iconManager = IconManager::getInstance();
    iconManager->registerNativeIcon("SwpProjectCategory",
        QStyle::SP_DirHomeIcon);
    iconManager->registerStyleIcon("SwpProjectCategory",
        ":/swp/icons/category_icon.png");
    iconManager->registerStyleIcon("SwpNewDocument",
        ":/swp/icons/new_document_icon.png");
    iconManager->registerStyleIcon("SwpNewDocumentMenu",
        ":/swp/icons/new_document_menu_icon.png");

    createActions();
    connectSlots();
}

ProjectNavigator::~ProjectNavigator()
{

}

QSharedPointer<IDocument> ProjectNavigator::document() const
{
    QSharedPointer<IDocument> ret;

    QTreeWidgetItem* item = currentItem();
    if (item != 0)
    {
        if (m_itemToDocumentMapping.contains(item))
        {
            ret = m_itemToDocumentMapping[item];
        }
        else
        {
            ret = m_itemToDocumentMapping[item->parent()];
        }
    }

    return ret;
}

QWidget* ProjectNavigator::widget() const
{
    return const_cast<ProjectNavigator* >(this);
}

QStringList ProjectNavigator::galleryGroups() const
{
    return QStringList();
}

QList<GalleryItem> ProjectNavigator::galleryItems(const QString& group) const
{
    return QList<GalleryItem>();
}

void ProjectNavigator::registerFocusListener(QObject* object)
{
    // do nothing
}

void ProjectNavigator::unregisterFocusListener(QObject* object)
{
    // do nothing
}

QSharedPointer<IContainerDocument> ProjectNavigator::selectedProject()
    const
{
    QSharedPointer<IContainerDocument> ret;

    QTreeWidgetItem* selection = currentItem();

    if (selection != 0)
    {
        if (m_itemToDocumentMapping.contains(selection))
        {
            const QSharedPointer<IDocument>& document =
                m_itemToDocumentMapping[selection];

            if (!document->isContainer())
            {
                ret = document->container();
            }
            else
            {
                ret = qSharedPointerDynamicCast<IContainerDocument>(document);
            }
        }
        else
        {
            QTreeWidgetItem* parent = selection->parent();

            if (m_itemToDocumentMapping.contains(parent))
            {
                ret = qSharedPointerDynamicCast<IContainerDocument>(
                    m_itemToDocumentMapping[parent]);
            }
        }
    }

    return ret;
}

bool ProjectNavigator::event(QEvent* event)
{
    bool ret = false;

    if (event->type() == SaveEvent::type())
    {
        saveSelectedProject();
        event->accept();
        ret = true;
    }
    else if (event->type() == QEvent::Close)
    {
        const QList<QSharedPointer<IContainerDocument> > oProjects =
            DocumentManager::getInstance()->openedProjects();

        bool dirty = false;

        foreach (const QSharedPointer<IContainerDocument>& project, oProjects)
        {
            if (project->isDirty() || project->isChildrenDirty())
            {
                dirty = true;
                break;
            }
        }

        if (dirty)
        {
            QMessageBox::StandardButton button = QMessageBox::question(
                qobject_cast<QWidget*>(parent()),
                tr("Close application"),
                tr("You have some unsaved work, do you wish"
                    " to discard changes and close the application?"),
                QMessageBox::Yes | QMessageBox::No);

            if (button == QMessageBox::No)
            {
                event->ignore();
            }
        }

        ret = true;
    }
    else
    {
        ret = QTreeWidget::event(event);
    }

    return ret;
}

void ProjectNavigator::addWindowMenuActions()
{
    MainWindow* mainWindow = MainWindow::getInstance();

    mainWindow->getMenu(PROJECT_MENU)->addSeparator();
    mainWindow->getMenu(PROJECT_MENU)->addAction(
        m_addDocumentMenuAction);
}

void ProjectNavigator::addToolBarActions(QToolBar* toolbar)
{
    toolbar->addSeparator();
    toolbar->addAction(
        m_addDocumentAction);
}

QList<QAction* > ProjectNavigator::perspectiveEnsuringActions() const
{
    QList<QAction* > ret;

    ret.append(m_addDocumentAction);
    ret.append(m_addDocumentMenuAction);

    return ret;
}

IEditor* ProjectNavigator::copy() const
{
    return new ProjectNavigator(QList<QAction* >());
}

void ProjectNavigator::contextMenuEvent(QContextMenuEvent* event)
{
    if (m_contextMenu == 0)
    {
        m_contextMenu = new QMenu(this);
    }

    foreach (QTreeWidgetItem* item, m_itemToDocumentMapping.keys())
    {
        if (visualItemRect(item).contains(event->pos()))
        {
            m_contextMenu->clear();

            const Document& doc =  m_itemToDocumentMapping[
                    item];
            const bool isContainer = doc->isContainer();
            const bool isLinked = doc->isLinked();
            const bool isOpened = !doc->isFuture();

            if (isContainer)
            {
                m_contextMenu->addAction(m_addDocumentAction);
            }

            m_contextMenu->addAction(m_renameAction);
            m_contextMenu->addSeparator();

            m_contextMenu->addAction(m_closeAction);
            m_closeAction->setEnabled(isOpened &&
                !DocumentManager::getInstance()->isOpenedToMemory(doc));

            if (!isContainer)
            {
                m_contextMenu->addAction(m_removeAction);
            }

            if (isContainer)
            {
                m_contextMenu->addSeparator();
                m_contextMenu->addAction(m_expandAll);
                m_contextMenu->addAction(m_collapseAll);
            }

            m_contextMenu->addSeparator();
            m_contextMenu->addAction(m_deleteAction);

            if (isLinked)
            {
                m_setLinks->setEnabled(isOpened);

                m_contextMenu->addSeparator();
                m_contextMenu->addAction(m_setLinks);
            }

            m_contextMenu->exec(event->globalPos());

            break;
        }
    }
}

void ProjectNavigator::startDrag(Qt::DropActions supportedActions)
{
    QTreeWidgetItem* source = currentItem();
    if (source != 0 && m_itemToDocumentMapping.contains(source))
    {
        m_draggedItem = source;

        if (m_itemToDocumentMapping[source]->isContainer())
        {
            m_draggedIsContainer = true;
            for (int i = 0; i < topLevelItemCount(); ++i)
            {
                if ((topLevelItem(i)->flags() & Qt::ItemIsDropEnabled) ==
                    Qt::ItemIsDropEnabled)
                {
                    topLevelItem(i)->setFlags(topLevelItem(i)->flags() ^
                        Qt::ItemIsDropEnabled);
                }
            }
        }
        else
        {
            m_draggedIsContainer = false;

            if ((invisibleRootItem()->flags() & Qt::ItemIsDropEnabled) ==
                Qt::ItemIsDropEnabled)
            {
                invisibleRootItem()->setFlags(invisibleRootItem()->flags() ^
                    Qt::ItemIsDropEnabled);
            }
        }
    }

    m_inDrag = true;
    QTreeWidget::startDrag(supportedActions);
    finishDrag();
}

void ProjectNavigator::dragMoveEvent(QDragMoveEvent* event)
{
    if (!m_draggedIsContainer)
    {
        if (m_draggedItem != 0)
        {
            QTreeWidgetItem* destination = itemAt(event->pos());

            if (destination != 0 && (destination->flags() &
                Qt::ItemIsSelectable) == Qt::ItemIsSelectable &&
                destination != m_draggedItem->parent()->parent())
            {
                QTreeWidget::dragMoveEvent(event);
            }
            else
            {
                event->ignore();
            }
        }
        else
        {
            event->ignore();
        }
    }
    else
    {
        QTreeWidget::dragMoveEvent(event);
    }
}

void ProjectNavigator::dropEvent(QDropEvent* event)
{
    if (m_draggedIsContainer)
    {
        QTreeWidget::dropEvent(event);

        for (int i = 0; i < topLevelItemCount(); ++i)
        {
            topLevelItem(i)->setFlags(topLevelItem(i)->flags() |
                Qt::ItemIsDropEnabled);
        }
    }
    else
    {
        invisibleRootItem()->setFlags(invisibleRootItem()->flags() |
            Qt::ItemIsDropEnabled);

        if (m_draggedItem != 0)
        {
            QTreeWidgetItem* destination = itemAt(event->pos());

            if (destination != 0)
            {
                const QSharedPointer<IDocument>& document =
                    m_itemToDocumentMapping[m_draggedItem];

                disconnect(document.data(), SIGNAL(dirtyChanged(bool)),
                    this, SLOT(ensureDocumentNameConsistency()));

                emit aboutToMove(m_itemToDocumentMapping[destination],
                    document);

                m_itemToDocumentMapping.remove(m_draggedItem);

                event->accept();
            }
            else
            {
                event->ignore();
            }
        }
        else
        {
            event->ignore();
        }
    }

    m_draggedIsContainer = false;
    m_inDrag = false;
}

void ProjectNavigator::createActions()
{
    IconManager* iconManager = IconManager::getInstance();

    m_closeAction = new QAction(tr("Close"), this);
    m_renameAction = new QAction(tr("Rename"),
        this);
    m_addDocumentAction = new QAction(tr("New Document"),
        this);
    m_addDocumentAction->setEnabled(false);
    if (iconManager->hasIcon("SwpNewDocument"))
    {
        m_addDocumentAction->setIcon(iconManager->icon("SwpNewDocument"));
    }

    m_addDocumentMenuAction = new QAction(tr("New Document"),
        this);
    m_addDocumentMenuAction->setShortcutContext(Qt::WindowShortcut);
    m_addDocumentMenuAction->setShortcut(QKeySequence::New);
    m_addDocumentMenuAction->setEnabled(false);
    if (iconManager->hasIcon("SwpNewDocumentMenu"))
    {
        m_addDocumentMenuAction->setIcon(iconManager->icon(
            "SwpNewDocumentMenu"));
    }

    m_removeAction = new QAction(tr("Remove"), this);
    m_deleteAction = new QAction(tr("Delete"), this);

    m_collapseAll = new QAction(tr("Collapse all"), this);
    m_expandAll = new QAction(tr("Expand all"), this);

    m_setLinks = new QAction(tr("Set links"), this);
}

void ProjectNavigator::connectSlots()
{
    connect(this, SIGNAL(itemActivated(QTreeWidgetItem*,int)),
        this, SLOT(openDocument(QTreeWidgetItem*,int)));

    connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
        this, SLOT(ensureDataConsistency(QTreeWidgetItem*,int)));

    connect(this, SIGNAL(selectionDeferred(QTreeWidgetItem*)),
            this, SLOT(selectItem(QTreeWidgetItem*)), Qt::QueuedConnection);

    connect(m_collapseAll, SIGNAL(triggered()),
        this, SLOT(collapseAll()));
    connect(m_expandAll, SIGNAL(triggered()),
        this, SLOT(expandAll()));

    connect(m_closeAction, SIGNAL(triggered()),
        this, SLOT(closeSelected()));

    connect(m_renameAction, SIGNAL(triggered()),
        this, SLOT(renameSelected()));

    connect(m_addDocumentAction, SIGNAL(triggered()),
            this, SLOT(addDocument()));
    connect(m_addDocumentMenuAction, SIGNAL(triggered()),
            this, SLOT(addDocument()));
    connect(m_removeAction, SIGNAL(triggered()),
            this, SLOT(removeDocument()));
    connect(m_deleteAction, SIGNAL(triggered()),
            this, SLOT(deleteDocument()));

    connect(m_setLinks, SIGNAL(triggered()),
            this, SLOT(setLinks()));
}

void ProjectNavigator::finishDrag()
{
    if (m_inDrag)
    {
        if (m_draggedIsContainer)
        {
            for (int i = 0; i < topLevelItemCount(); ++i)
            {
                topLevelItem(i)->setFlags(topLevelItem(i)->flags() |
                    Qt::ItemIsDropEnabled);
            }
        }
        else
        {
            invisibleRootItem()->setFlags(invisibleRootItem()->flags() |
                Qt::ItemIsDropEnabled);
        }

        m_draggedIsContainer = false;
        m_inDrag = false;
    }
}

// public slots
void ProjectNavigator::open(const QSharedPointer<IDocument>& document)
{
    Q_ASSERT(!document.isNull() && document->isContainer());

    IconManager* iconManager = IconManager::getInstance();

    QSharedPointer<SwpProjectDocument> _document =
        qSharedPointerDynamicCast<SwpProjectDocument>(document);

    Q_ASSERT(!document.isNull());

    QTreeWidgetItem* projectItem = new QTreeWidgetItem(
        QStringList(QString("%1%2").arg(_document->name()).arg(
            _document->isDirty() ? "*" : "")));
    projectItem->setIcon(0, iconManager->icon(
        _document->mimeType().iconName()));
    projectItem->setData(0, Qt::UserRole, _document->name());
    projectItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable |
        Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled);

    connect(document.data(), SIGNAL(dirtyChanged(bool)),
        this, SLOT(ensureDocumentNameConsistency()));

    m_itemToDocumentMapping[projectItem] = document;

    addTopLevelItem(projectItem);

    const core::DocumentCategories categories = _document->categories();

    MimeTypeManager* mm = MimeTypeManager::getInstance();

    foreach (const QString& category, categories)
    {
        const QList<MimeType> _mimeTypes =
            _document->categoryMimeTypes(category);
        if (_mimeTypes.contains(mm->mimeType(CLASS_DIAGRAM_MIMETYPE)) ||
            _mimeTypes.contains(mm->mimeType(ACTIVITY_DIAGRAM_MIMETYPE)))
        {
            QTreeWidgetItem* categoryItem = new QTreeWidgetItem(
                QStringList(category));
            categoryItem->setIcon(0, iconManager->icon("SwpProjectCategory"));
            categoryItem->setFlags(Qt::ItemIsEnabled);
            projectItem->addChild(categoryItem);
            categoryItem->setExpanded(true);

            QList<QSharedPointer<IDocument> > childDocuments =
                _document->documents(category);

            foreach (const QSharedPointer<IDocument>& childDocument,
                childDocuments)
            {
                QTreeWidgetItem* documentItem = new QTreeWidgetItem(
                    QStringList(QString("%1%2").arg(childDocument->name()).arg(
                        childDocument->isDirty() ? "*" : "")));
                documentItem->setIcon(0, iconManager->icon(
                    childDocument->mimeType().iconName()));
                documentItem->setData(0, Qt::UserRole, childDocument->name());
                documentItem->setFlags(Qt::ItemIsSelectable |
                    Qt::ItemIsEditable | Qt::ItemIsDragEnabled |
                    Qt::ItemIsEnabled);

                m_itemToDocumentMapping[documentItem] = childDocument;

                categoryItem->addChild(documentItem);

                connect(childDocument.data(), SIGNAL(dirtyChanged(bool)),
                    this, SLOT(ensureDocumentNameConsistency()));
            }
        }
    }

    setCurrentItem(projectItem);
    scrollToItem(projectItem);
    expandItem(projectItem);

    m_addDocumentAction->setEnabled(true);
    m_addDocumentMenuAction->setEnabled(true);

    foreach (QAction* action, m_openedProjectDependentActions)
    {
        action->setEnabled(true);
    }
}

void ProjectNavigator::add(const QSharedPointer<core::IDocument>& project,
    const QSharedPointer<core::IDocument>& document)
{
    const QSharedPointer<IContainerDocument>& _project =
        qSharedPointerDynamicCast<IContainerDocument>(project);

    foreach (QTreeWidgetItem* item,
        m_itemToDocumentMapping.keys())
    {
        if (project == m_itemToDocumentMapping[item])
        {
            item->setText(0, QString("%1%2").arg(project->name()).arg(
                              project->isDirty() ? "*" : ""));

            for (int i = 0; i < item->childCount(); ++i)
            {
                QTreeWidgetItem* category = item->child(i);
                if (category->text(0) == _project->categoryFor(document))
                {
                    QTreeWidgetItem* documentItem = new QTreeWidgetItem(
                        QStringList(QString("%1%2").arg(document->name()).arg(
                            document->isDirty() ? "*" : "")));
                    documentItem->setIcon(0, IconManager::getInstance()->icon(
                        document->mimeType().iconName()));
                    documentItem->setData(0, Qt::UserRole, document->name());
                    documentItem->setFlags(Qt::ItemIsSelectable |
                        Qt::ItemIsEditable | Qt::ItemIsDragEnabled |
                        Qt::ItemIsEnabled);

                    m_itemToDocumentMapping[documentItem] = document;

                    category->addChild(documentItem);
                    category->setExpanded(true);

                    if (!m_inDrag)
                    {
                        setCurrentItem(documentItem);
                    }
                    else
                    {
                        emit selectionDeferred(documentItem);
                    }

                    connect(document.data(), SIGNAL(dirtyChanged(bool)),
                        this, SLOT(ensureDocumentNameConsistency()));

                    break;
                }
            }

            break;
        }
    }
}

void ProjectNavigator::remove(const QSharedPointer<core::IDocument>& project,
    const QSharedPointer<core::IDocument>& document)
{
    foreach (QTreeWidgetItem* item,
        m_itemToDocumentMapping.keys())
    {
        if (document == m_itemToDocumentMapping[item])
        {
            m_itemToDocumentMapping.remove(item);

            disconnect(document.data(), SIGNAL(dirtyChanged(bool)),
                this, SLOT(ensureDocumentNameConsistency()));

            delete item;

            break;
        }
    }
}

void ProjectNavigator::remove(const QSharedPointer<core::IDocument>& document)
{
    foreach (QTreeWidgetItem* item,
        m_itemToDocumentMapping.keys())
    {
        if (document == m_itemToDocumentMapping[item])
        {
            m_itemToDocumentMapping.remove(item);

            disconnect(document.data(), SIGNAL(dirtyChanged(bool)),
                this, SLOT(ensureDocumentNameConsistency()));

            delete item;

            break;
        }
    }
}

void ProjectNavigator::close(const QSharedPointer<core::IDocument>& document)
{
    foreach (QTreeWidgetItem* item,
        m_itemToDocumentMapping.keys())
    {
        if (document == m_itemToDocumentMapping[item])
        {
            if (document->isContainer())
            {
                for (int i = 0; i < topLevelItemCount(); ++i)
                {
                    if (item == topLevelItem(i))
                    {
                        takeTopLevelItem(i);
                        break;
                    }
                }

                QSharedPointer<SwpProjectDocument> _document =
                    qSharedPointerDynamicCast<SwpProjectDocument>(document);

                foreach (const QString& category, _document->categories())
                {
                    foreach (const QSharedPointer<IDocument>& childDocument,
                        _document->documents(category))
                    {
                        disconnect(childDocument.data(),
                            SIGNAL(dirtyChanged(bool)),
                            this, SLOT(ensureDocumentNameConsistency()));
                    }
                }

                disconnect(document.data(),
                    SIGNAL(dirtyChanged(bool)),
                    this, SLOT(ensureDocumentNameConsistency()));

                m_itemToDocumentMapping.remove(item);
            }          

            break;
        }
    }

    if (topLevelItemCount() == 0)
    {
        m_addDocumentAction->setEnabled(false);
        m_addDocumentMenuAction->setEnabled(false);

        foreach (QAction* action, m_openedProjectDependentActions)
        {
            action->setEnabled(false);
        }
    }
}

void ProjectNavigator::tryToSelectProject(
    const QSharedPointer<IContainerDocument>& project)
{
    const QSharedPointer<IDocument> _project =
        qSharedPointerDynamicCast<IDocument>(project);

    for (int index = 0; index < topLevelItemCount(); ++index)
    {
         QTreeWidgetItem* item = topLevelItem(index);

         if (m_itemToDocumentMapping.contains(item) &&
            m_itemToDocumentMapping[item] == _project)
         {
             selectItem(item);
             break;
         }
    }
}

void ProjectNavigator::tryToSelectDocument(
    const QSharedPointer<IDocument>& document)
{
    foreach (QTreeWidgetItem* item, m_itemToDocumentMapping.keys())
    {
        if (m_itemToDocumentMapping[item] == document)
        {
            selectItem(item);
            break;
        }
    }
}

void ProjectNavigator::closeSelected()
{
    QTreeWidgetItem* selection = currentItem();

    if (m_itemToDocumentMapping.contains(selection))
    {
        emit aboutToClose(m_itemToDocumentMapping[selection]);
    }
}

void ProjectNavigator::renameSelected()
{
    QTreeWidgetItem* selection = currentItem();

    if (m_itemToDocumentMapping.contains(selection))
    {
        editItem(selection);
    }
}

void ProjectNavigator::closeSelectedProject()
{
    QTreeWidgetItem* selection = currentItem();

    if (selection != 0)
    {
        if (m_itemToDocumentMapping.contains(selection))
        {
            QSharedPointer<IDocument>& document = m_itemToDocumentMapping[
                selection];

            if (!document->isContainer())
            {
                document = qSharedPointerDynamicCast<IDocument>(
                    document->container());
            }

            emit aboutToClose(document);
        }
        else
        {
            QTreeWidgetItem* parent = selection->parent();

            if (m_itemToDocumentMapping.contains(parent))
            {
                emit aboutToClose(m_itemToDocumentMapping[parent]);
            }
        }
    }
}

void ProjectNavigator::saveSelectedProject()
{
    QTreeWidgetItem* selection = currentItem();

    if (m_itemToDocumentMapping.contains(selection))
    {
        QSharedPointer<IDocument> document = m_itemToDocumentMapping[
            selection];

        if (!document->isContainer())
        {
            document = qSharedPointerDynamicCast<IDocument>(
                document->container());
        }

        emit aboutToSave(document);
    }
    else
    {
        QTreeWidgetItem* parent = selection->parent();

        if (m_itemToDocumentMapping.contains(parent))
        {
            emit aboutToSave(m_itemToDocumentMapping[parent]);
        }
    }
}

void ProjectNavigator::importToSelectedProject()
{
    QTreeWidgetItem* selection = currentItem();

    QSharedPointer<IContainerDocument> project;

    if (m_itemToDocumentMapping.contains(selection))
    {
        QSharedPointer<IDocument> document = m_itemToDocumentMapping[
            selection];

        if (!document->isContainer())
        {
            document = qSharedPointerDynamicCast<IDocument>(
                document->container());
        }

        project = qSharedPointerDynamicCast<IContainerDocument>(document);
    }
    else
    {
        QTreeWidgetItem* parent = selection->parent();

        if (m_itemToDocumentMapping.contains(parent))
        {
            project = qSharedPointerDynamicCast<IContainerDocument>(
                m_itemToDocumentMapping[parent]);
        }
    }

    if (!project.isNull())
    {
        XmiImportDialog dialog(project, MainWindow::getInstance());
        dialog.exec();
    }
}

// private slots
void ProjectNavigator::addDocument()
{
    QTreeWidgetItem* selection = currentItem();

    if (selection != 0)
    {
        QSharedPointer<IDocument> project;

        if (m_itemToDocumentMapping.contains(selection))
        {
            project = m_itemToDocumentMapping[
                selection];

            if (!project->isContainer())
            {
                project = m_itemToDocumentMapping[
                    selection->parent()->parent()];
            }
        }
        else
        {
            project = m_itemToDocumentMapping[selection->parent()];
        }

         MimeTypeManager* mm = MimeTypeManager::getInstance();

        emit aboutToAdd(project,
            (QList<MimeType>() << mm->mimeType(
                CLASS_DIAGRAM_MIMETYPE)
             << mm->mimeType(ACTIVITY_DIAGRAM_MIMETYPE)),
                DocumentManager::None);
    }
}

void ProjectNavigator::removeDocument()
{
    QTreeWidgetItem* selection = currentItem();

    if (selection != 0)
    {
        QSharedPointer<IDocument> project;
        QSharedPointer<IDocument> document;

        if (m_itemToDocumentMapping.contains(selection))
        {
            document = m_itemToDocumentMapping[
                selection];

            project = m_itemToDocumentMapping[selection->parent()->parent()];

            emit aboutToRemove(project, document);
        }
    }
}

void ProjectNavigator::deleteDocument()
{
    QTreeWidgetItem* selection = currentItem();

    if (selection != 0)
    {
        emit aboutToDelete(m_itemToDocumentMapping[selection]);
    }
}

void ProjectNavigator::openDocument(QTreeWidgetItem* item, int column)
{
    if (m_itemToDocumentMapping.contains(item))
    {
        QSharedPointer<IDocument>& document = m_itemToDocumentMapping[item];

        if (!document->isContainer() &&
            !DocumentManager::getInstance()->isOpenedToMemory(document))
        {
            emit aboutToOpen(document);
        }
    }
}

void ProjectNavigator::ensureDataConsistency(QTreeWidgetItem* item,
    int column)
{
    if (m_itemToDocumentMapping.contains(item))
    {
        QSharedPointer<IDocument>& document = m_itemToDocumentMapping[item];

        const QString name = item->data(0, Qt::UserRole).toString();
        if (document->name() != name)
        {
            document->rename(item->data(0, Qt::UserRole).toString());
            document->setDirty();
            item->setText(0, QString("%1%2").arg(document->name()).arg(
                document->isDirty() ? "*" : ""));
            item->setData(0, Qt::UserRole, document->name());
        }
    }
}

void ProjectNavigator::ensureDocumentNameConsistency()
{
    foreach (QTreeWidgetItem* item, m_itemToDocumentMapping.keys())
    {
        const QSharedPointer<IDocument> document =
            m_itemToDocumentMapping[item];

        item->setText(0, QString("%1%2").arg(document->name()).arg(
            document->isDirty() ? "*" : ""));
    }
}

void ProjectNavigator::selectItem(QTreeWidgetItem* item)
{
    if (item != 0)
    {
        setCurrentItem(item);
    }
}

void ProjectNavigator::setLinks()
{
    QTreeWidgetItem* item = currentItem();
    if (m_itemToDocumentMapping.contains(item))
    {
        const QSharedPointer<IDocument> document =
            m_itemToDocumentMapping[item];

        Q_ASSERT(document->isLinked()); // the setLinks action is based on isLinked

        const QSharedPointer<ILinkedDocument> _document =
            qSharedPointerDynamicCast<ILinkedDocument>(document);

        const QList<QSharedPointer<IDocument> > canBeRemoved =
            _document->links();

        const QSharedPointer<IContainerDocument> container =
            _document->container();

        Q_ASSERT(!container.isNull()); // container can't be null

        QList<QSharedPointer<IDocument> > canBeAdded;

        foreach (const QString& category, container->categories())
        {
            const QList<MimeType> cMimeTypes = container->categoryMimeTypes(
                category);
            const QList<MimeType> linkTypes =_document->linksTo();

            foreach (const MimeType& mimeType, linkTypes)
            {
                if (cMimeTypes.contains(mimeType))
                {
                    const QList<QSharedPointer<IDocument> > documents =
                            container->documents(category);

                    foreach (const Document& candidate, documents)
                    {
                        if (linkTypes.contains(candidate->mimeType()) &&
                            !canBeRemoved.contains(candidate))
                        {
                            canBeAdded.append(candidate);
                        }
                    }
                }
            }
        }

        LinksEditorDialog dialog(canBeAdded, canBeRemoved, this);
        const int result = dialog.exec();

        if (result == QDialog::Accepted)
        {
            const QList<QSharedPointer<IDocument> > added = dialog.added();
            const QList<QSharedPointer<IDocument> > removed =
                dialog.removed();

            foreach (const Document& addedDocument, added)
            {
                _document->addLink(addedDocument);
            }

            foreach (const Document& removedDocument, removed)
            {
                _document->removeLink(removedDocument);
            }
        }
    }
}
